package cron

import (
	"cloudops/src/config"
	"cloudops/src/models"
	"context"
	"fmt"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/rds"
	"github.com/gammazero/workerpool"
	"github.com/google/uuid"
	"go.uber.org/zap"
	"math/rand"
	"sync"
	"time"
)

// RunSyncCloudResourceRds 同步RDS云资源
func (cm *CronManager) RunSyncCloudResourceRds(ctx context.Context) {
	start := time.Now()

	if !cm.GetRdsSynced() {
		cm.Sc.Logger.Info("rds上次同步还未完成，本次cron跳过")
		return
	}

	// 上传完成，开始新的一轮同步
	cm.SetRdsSynced(false)
	allMap := sync.Map{}
	wp := workerpool.New(5) // 5个goroutine同时执行
	for _, alic := range cm.Sc.PublicCloudSyncC.AliCloud {
		alic := alic
		wp.Submit(func() {
			cm.RunSyncOneCloudRdsAli(alic, &allMap)
		})
	}
	// todo: 其他厂商数据库
	wp.StopWait()

	// 计算
	toAddSet := make([]*models.ResourceRds, 0)
	toModSet := make([]*models.ResourceRds, 0)
	toDelUid := []string{}
	localUidSet := make(map[string]struct{})

	var toAddNum, toModNum, toDelNum int
	var suAddNum, suModNum, suDelNum int

	// 获取本地的uid队友的hashM
	dbUidHashM, err := models.GetResourceRdsUidAndHash()
	if err != nil {
		cm.Sc.Logger.Error("rds 获取本地的uid对应的hashM", zap.Error(err))
		return
	}

	// 遍历阿里云的uid和hashM和本地数据库的uid和hashM对比，计算需要添加、修改和删除、更新
	rangeFunc := func(k, v interface{}) bool {
		uid := k.(string)
		resourceObj := v.(*models.ResourceRds)
		localUidSet[uid] = struct{}{}
		dbHash, ok := dbUidHashM[uid]

		if !ok {
			toAddSet = append(toAddSet, resourceObj)
			toAddNum++
		} else if dbHash != resourceObj.Hash {
			toModSet = append(toModSet, resourceObj)
			toModNum++
		}
		return true
	}
	allMap.Range(rangeFunc)

	// 遍历本地数据库的uid和hashM
	for _, uid := range dbUidHashM {
		if _, ok := localUidSet[uid]; !ok {
			toDelUid = append(toDelUid, uid)
			toDelNum++
		}
	}

	// 下面开始执行同步
	for _, obj := range toAddSet {
		err := obj.CreateOne()
		if err != nil {
			cm.Sc.Logger.Error("rds Add new resources failed",
				zap.Any("uid", obj.InstanceId),
				zap.Error(err),
			)
			continue
		}
		cm.Sc.Logger.Info("rds Successfully added resources",
			zap.Any("uid", obj.InstanceId),
		)
		suAddNum++
	}

	// 更新逻辑
	for _, obj := range toModSet {
		err := obj.UpdateOne()
		if err != nil {
			cm.Sc.Logger.Error("rds Update resources failed",
				zap.Any("id", obj.InstanceId),
				zap.Error(err),
			)
			continue
		}
		cm.Sc.Logger.Info("rds Successfully updated resources",
			zap.Any("id", obj.InstanceId),
			zap.Any("name", obj.InstanceName),
		)
		suModNum++
	}

	// 删除逻辑
	for _, uid := range toDelUid {
		// 先获取InstanceId
		dbObj, err := models.GetResourceRdsByInstanceId(uid)
		if err != nil {
			cm.Sc.Logger.Error("rds Get resource by uid failed",
				zap.Any("uid", uid),
				zap.Error(err),
			)
			continue
		}
		err = dbObj.DeleteOne()
		if err != nil {
			cm.Sc.Logger.Error("rds Delete resource failed",
				zap.Any("id", dbObj.ID),
				zap.Error(err),
			)
			continue
		}
		cm.Sc.Logger.Info("rds Successfully deleted resources",
			zap.Any("id", dbObj.ID),
		)
		suDelNum++
	}

	// 统计耗时
	tookSeconds := time.Since(start).Seconds()
	cm.Sc.Logger.Info("rds Sync cloud resources finished",
		zap.Any("公有云总数", len(localUidSet)),
		zap.Any("数据库", len(dbUidHashM)),
		zap.Any("toAddNum", toAddNum),
		zap.Any("toModNum", toModNum),
		zap.Any("toDelNum", toDelNum),
		zap.Any("suAddNum", suAddNum),
		zap.Any("suModNum", suModNum),
		zap.Any("suDelNum", suDelNum),
		zap.Any("timeTook", tookSeconds),
	)

	// 本地同步结束，标记完成
	cm.SetRdsSynced(true)
}

func (cm *CronManager) RunSyncOneCloudRdsAli(alic *config.AliCloud, allMap *sync.Map) {
	cm.Sc.Logger.Info("rds sync ali cloud resources start",
		zap.Any("regionId", alic.RegionId),
		zap.Any("accessKeyId", alic.AccessKeyId),
		zap.Any("accessKeySecret", alic.AccessKeySecret),
	)

	// 直接返回mock数据
	rand.Seed(time.Now().UnixNano())
	allNum := MockListRdsResponse(allMap)
	cm.Sc.Logger.Info("rds sync ali cloud resources finished",
		zap.Any("allNum", allNum),
		zap.Any("regionId", alic.RegionId),
		zap.Any("accessKeyId", alic.AccessKeyId),
		zap.Any("accessKeySecret", alic.AccessKeySecret),
	)
	return

	// 新建rds client
	client, err := rds.NewClientWithAccessKey(
		alic.RegionId,
		alic.AccessKeyId,
		alic.AccessKeySecret,
	)
	if err != nil {
		cm.Sc.Logger.Error("RDS client initialization failed",
			zap.Error(err),
			zap.Any("regionId", alic.RegionId),
			zap.Any("accessKeyId", alic.AccessKeyId),
			zap.Any("accessKeySecret", alic.AccessKeySecret),
		)
		return
	}
	cm.Sc.Logger.Info("RDS client initialization successful", zap.Any("client", client.SourceIp))
	resp, err := client.DescribeDBInstances(&rds.DescribeDBInstancesRequest{})
	if err != nil {
		cm.Sc.Logger.Error("RDS DescribeDBInstances failed",
			zap.Any("regionId", alic.RegionId),
			zap.Any("accessKeyId", alic.AccessKeyId),
			zap.Any("accessKeySecret", alic.AccessKeySecret),
		)
		return
	}
	cm.Sc.Logger.Info("RDS DescribeDBInstances successful",
		zap.Int("rds", resp.TotalRecordCount),
		zap.Any("regionId", alic.RegionId),
		zap.Any("accessKeyId", alic.AccessKeyId),
		zap.Any("accessKeySecret", alic.AccessKeySecret),
	)

	cloudIns := resp.Items.DBInstance
	for _, ins := range cloudIns {
		dbIns := cm.ConvertRdsCloudAli(ins)
		allMap.Store(dbIns.InstanceId, dbIns)
	}
	return
}

// 模拟同步数据
func MockListRdsResponse(allMap *sync.Map) int {
	rand.Seed(time.Now().UnixNano())
	// 公共的
	randEnv := []string{"dev", "press", "stage", "prod"}
	randVendor := []string{"阿里云", "华为云", "腾讯云", "aws", "世纪互联"}
	randVpcId := []string{"vpc-01", "vpc-02", "vpc-03"}
	randZoneId := []string{"bj1", "bj4", "hangzhou1", "sh1", "sh3"}
	// 标签tags
	randTagKeys := []string{"arch", "idc", "os", "job", "env", "cluster", "type"}
	randTagValues := []string{"linux", "beijing", "windows", "shanghai", "arm64", "amd64", "darwin", "shijihulian"}

	// 独特的
	randEngine := []string{"mysql", "mariadb", "postgresql", "oracle"}
	randDBInstanceClass := []string{"ecs.g8a.large", "ecs.g7se.large", "ecs.g7a-nps1.16xlarge", "ecs.g7ne.12small", "ecs.c8a.8xlarge", "ecs.c7se.6xlarge"}
	randDBInstanceType := []string{"Primary", "Readonly", "Guard", "Temp", "Master", "Slave"}
	randEngineVersion := []string{"5.5", "5.7", "7.0", "8.0", "20.0"}

	// 目的是4选1 返回0-3的数组
	// 比如8-15 15-8 +8
	frn := func(n int) int {
		rand.Seed(time.Now().UnixNano())
		return rand.Intn(n)
	}

	// 每次host数量 10-30个
	frNum := func() int {
		rand.Seed(time.Now().UnixNano())
		return int(rand.Int63n(40-20) + 10)
	}
	all := 0
	// 每次固定的实例，它的uid 要一致
	for i := 0; i < 100; i++ {
		objOne := &models.ResourceRds{}

		// 固定id
		objOne.InstanceId = fmt.Sprintf("rds-id-fixed-%v", i)
		objOne.InstanceName = fmt.Sprintf("rds-name-fixed-%v", i)

		// 公共字段
		objOne.Env = randEnv[frn(len(randEnv)-1)]
		objOne.Vendor = randVendor[frn(len(randVendor)-1)]
		objOne.ZoneId = randZoneId[frn(len(randZoneId)-1)]
		objOne.VpcId = randVpcId[frn(len(randVpcId)-1)]
		tags := []string{}
		for _, tagKey := range randTagKeys {
			tagValue := randTagValues[frn(len(randTagValues)-1)]
			oneTag := fmt.Sprintf("%s=%s", tagKey, tagValue)
			tags = append(tags, oneTag)
		}
		objOne.Tags = tags

		innerIp := fmt.Sprintf("1%v.%v.%v.%v", i, i, i, i)
		pubIp := fmt.Sprintf("2%v.%v.%v.%v", i, i, i, i)

		objOne.PrivateIpAddress = []string{innerIp}
		objOne.PublicIpAddress = []string{pubIp}

		// 独特字段
		objOne.Engine = randEngine[frn(len(randEngine)-1)]
		objOne.EngineVersion = randEngineVersion[frn(len(randEngineVersion)-1)]
		objOne.DBInstanceClass = randDBInstanceClass[frn(len(randDBInstanceClass)-1)]
		objOne.DBInstanceType = randDBInstanceType[frn(len(randDBInstanceType)-1)]

		objOne.Hash = objOne.GenHash()
		allMap.Store(objOne.InstanceId, objOne)
		all++
	}

	// 下面是纯随机的
	for i := 0; i < frNum(); i++ {
		objOne := &models.ResourceRds{}

		objOne.InstanceId = uuid.New().String()
		objOne.InstanceName = fmt.Sprintf("rds-name-%v-%v", i, objOne.InstanceId[:10])

		// 公共字段
		objOne.Env = randEnv[frn(len(randEnv)-1)]
		objOne.Vendor = randVendor[frn(len(randVendor)-1)]
		objOne.VpcId = randVpcId[frn(len(randVpcId)-1)]
		tags := []string{}
		for _, tagKey := range randTagKeys {
			tagValue := randTagValues[frn(len(randTagValues)-1)]
			oneTag := fmt.Sprintf("%s=%s", tagKey, tagValue)
			tags = append(tags, oneTag)
		}
		objOne.Tags = tags

		innerIp := fmt.Sprintf("1%v.%v.%v.%v", i, i, i, i)
		pubIp := fmt.Sprintf("2%v.%v.%v.%v", i, i, i, i)
		objOne.PrivateIpAddress = []string{innerIp}
		objOne.PublicIpAddress = []string{pubIp}

		// 独特字段
		objOne.Engine = randEngine[frn(len(randEngine)-1)]
		objOne.EngineVersion = randEngineVersion[frn(len(randEngineVersion)-1)]
		objOne.DBInstanceClass = randDBInstanceClass[frn(len(randDBInstanceClass)-1)]
		objOne.DBInstanceType = randDBInstanceType[frn(len(randDBInstanceType)-1)]

		objOne.Hash = objOne.GenHash()
		allMap.Store(objOne.InstanceId, objOne)
		all++
	}
	return all
}

// 转换rds字段到db中
func (cm *CronManager) ConvertRdsCloudAli(ins rds.DBInstance) *models.ResourceEcs {
	return nil
}
